﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;

namespace Sofire.DataComm.Net
{
    /// <summary>
    /// 一个可靠套接字的客户端实现。
    /// </summary>
    public class SocketClient : SocketBase
    {
        private TimeSpan _ConnectTimeout = new TimeSpan(0, 0, 3);
        /// <summary>
        /// 设置或获取一个值，表示连接到远程服务端的超时时间。
        /// </summary>
        public TimeSpan ConnectTimeout
        {
            get
            {
                return this._ConnectTimeout;
            }
            set
            {
                this._ConnectTimeout = value;
            }
        }

        private Socket _Server;
        /// <summary>
        /// 获取服务端的 <see cref="System.Net.Sockets"/>。
        /// </summary>
        public Socket Server
        {
            get
            {
                return this._Server;
            }
        }

        /// <summary>
        /// 指定网络地址以及通讯协议，初始化 <see cref="Sofire.DataComm.Net.SocketClient"/> 的新实例。
        /// </summary>
        /// <param name="mode">通讯协议的类型。</param>
        /// <param name="ip">网络 IP。</param>
        /// <param name="port">网络端口。</param>
        public SocketClient(ProtocolMode mode, string ip, int port)
            : this(new SocketInfo(mode, ip, port)) { }

        /// <summary>
        /// 指定网络端口以及通讯协议，默认 <see cref="System.Net.IPAddress.Loopback"/> 地址，初始化 <see cref="Sofire.DataComm.Net.SocketClient"/> 的新实例。
        /// </summary>
        /// <param name="mode">通讯协议的类型。</param>
        /// <param name="port">网络端口。</param>
        public SocketClient(ProtocolMode mode, int port)
            : this(new SocketInfo(mode, port)) { }

        /// <summary>
        /// 指定套接字信息，初始化 <see cref="Sofire.DataComm.Net.SocketClient"/> 的新实例。
        /// </summary>
        /// <param name="socketInfo">套接字的信息。</param>
        public SocketClient(SocketInfo socketInfo)
            : base(socketInfo) { }
        
        /// <summary>
        /// 可靠的套接字 接收数据 或 发送数据 时抛出的异常。当发生异常后，<paramref name="fromSocket"/> 将停止接收。
        /// </summary>
        /// <param name="fromSocket">数据来源的 <see cref="System.Net.Sockets.Socket"/>。</param>
        /// <param name="exception">接收时抛出的异常。</param>
        protected override void OnSocketFaild(Socket fromSocket, Exception exception)
        {
            base.OnSocketFaild(fromSocket, exception);
            this.Close();
        }

        /// <summary>
        /// 打开通讯时发生。
        /// </summary>
        protected override void OnOpen()
        {
            //ConnectTimeoutObject.Reset();
            ManualResetEvent ConnectTimeoutObject = new ManualResetEvent(false);
            Exception connectEx = null;
            bool isConnected = false;
            bool isTimeout = false;
            this._Server = this._SocketInfo.CreateSocket();

            this._Server.BeginConnect(this._SocketInfo.EndPoint, (ar) =>
            {
                try
                {
                    this._Server.EndConnect(ar);
                    //在超时以后进行连接的
                    if(!isTimeout)
                    {
                        this.BeginReceive(this._Server);
                    }
                }
                catch(Exception ex)
                {
                    connectEx = ex;
                }
                isConnected = true;
                ConnectTimeoutObject.Set();
            }, null);

            ConnectTimeoutObject.WaitOne(this._ConnectTimeout,true);

            if(isConnected)
            {
                if(connectEx != null) throw connectEx;
                return;
            }
            isTimeout = true;
            throw new TimeoutException();
        }

        /// <summary>
        /// 关闭通讯时发生。
        /// </summary>
        protected override void OnClose()
        {
            if(this._Server == null) return;
            this._Server.Close();
            this._Server = null;
        }

        /// <summary>
        /// 指定字节数组，开始异步发送数据。
        /// </summary>
        /// <param name="buffer">字节数组。</param>
        /// <param name="tag">用户自定义数据。</param>
        public virtual void BeginSend(byte[] buffer, object tag)
        {
            this.BeginSendTo(buffer, this._Server, tag);
        }

        /// <summary>
        /// 指定字节数组，开始异步发送数据。
        /// </summary>
        /// <param name="buffer">字节数组。</param>
        public virtual void BeginSend(byte[] buffer)
        {
            this.BeginSendTo(buffer, this._Server, null);
        }
    }
}
